using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Moq;
using NUnit.Framework;
using SpecMaker.Core;
using SpecMaker.Core.Assertions;
using SpecMaker.Core.Interfaces;
using SpecMaker.UnitTests.SupportClasses;

namespace SpecMaker.UnitTests
{
    
    public class BaseReportFactoryContext
    {
        protected SpecResult[] spec;
        [SetUp]
        public virtual void SetUp()
        {
            

        }
        protected SpecResult[] IntializeSpec(params RuleResult[] result)
        {
            return GetBankAccountTestSpec(result);
        }
        
        protected SpecResult[] IntializeSpec(RuleStatus testresult)
        {
            var ruleresults = new RuleResult()
            {
                Description = "Run Ofac Check",
                Status = testresult
            };
            return GetBankAccountTestSpec(ruleresults);
        }

        private SpecResult[] GetBankAccountTestSpec(params RuleResult[] ruleresults)
        {
            return  new[]
                        {
                            new SpecResult("BankAccount", "When Totaling Transactions", ruleresults)
                        };
        }
    }
    [TestFixture]
    public class SpecHtmlReportFactoryWhenGeneratingReport : BaseReportFactoryContext
    {
        private string results;
        private StubFileWrapper consolewrapper;

        [SetUp]
        public override void SetUp()
        {
            base.SetUp();
            spec = IntializeSpec(RuleStatus.Passed);
             consolewrapper = new StubFileWrapper();
            var reportfactory = new HtmlReporter(consolewrapper);
            reportfactory.FromSpecContainers(spec);
            results = consolewrapper.StringCalled;
        }
        [Test]
        public void should_write_type_under_spec()
        {
            // using new Assert extension method
            results.ShouldContain("Item under specification: " + spec[0].SpecObject);
           //Assert.IsTrue(results.ShouldContain("Item under specification: " + spec[0].SpecObject));
        }

        [Test]
        public void should_state_context_of_test()
        {

            Assert.IsTrue(results.Contains("Situation: " + spec[0].Context));
        }

        [Test]
        public void should_show_all_rules_with_itshould_then_description()
        {
            for (int i = 0; i < spec.Length; i++)
            {
                Assert.IsTrue(results.Contains("It Should " + spec[0].Results[i].Description));
                Assert.IsTrue(results.Contains(spec[0].Results[i].Status.DisplayName));
            }

        }

        [Test]
        public void should_open_file()
        {
            Assert.IsTrue(consolewrapper.OpenCalled);
            
        }

        [Test]
        public void should_close_file()
        {
            Assert.IsTrue(consolewrapper.CloseCalled);

        }
    }

    [TestFixture]
    public class SpecConsoleReportFactoryWhenGeneratingReport :BaseReportFactoryContext
    {
      
        private string results;


        [SetUp]
        public override void SetUp()
        {
            base.SetUp();
            spec = IntializeSpec(RuleStatus.Passed);
            var consolewrapper = new StringConsoleWrapper();
            ConsoleReportFactory reportfactory = new ConsoleReportFactory(consolewrapper);
            reportfactory.FromSpecContainers(spec);
            results = consolewrapper.Written;
        }

        [Test]
        public void should_write_type_under_spec()
        {
            Assert.IsTrue(results.Contains(spec[0].SpecObject));
            
        }

        [Test]
        public void should_state_context_of_test_with_tab_in_front()
        {

            Assert.IsTrue(results.Contains("\t"+spec[0].Context));
        }

        [Test]
        public void should_show_all_rules_with_tab_dash_rule()
        {
            for (int i = 0; i < spec.Length; i++)
            {
                Assert.IsTrue(results.Contains("\t - It Should " +spec[0].Results[i].Description + " - " + spec[0].Results[i].Status));
            }
             
        }


     
       
    }
    [TestFixture]
    public class SpecConsoleReportFactoryWhenGeneratingReportHaserrors : BaseReportFactoryContext
    {

         
        private Mock<IConsoleWrapper> consolewrapper;


        [SetUp]
        public override void SetUp()
        {
            base.SetUp();
              
            spec = IntializeSpec(new RuleResult()
                                     {
                                         Description = "blah",
                                         ErrorMessage = "this is our error message",
                                         Status = RuleStatus.Error
                                     });
              consolewrapper = new Mock<IConsoleWrapper>();
            consolewrapper.SetupGet(x => x.ForegroundColor).Returns(ConsoleColor.DarkMagenta);
            
                ConsoleReportFactory reportfactory = new ConsoleReportFactory(consolewrapper.Object);
            reportfactory.FromSpecContainers(spec);
             
        }

        [Test]
        public void should_have_error_message_in_output()
        {

            consolewrapper.Verify(console=>console.WriteLine(It.Is<string>(x=>x.Contains("this is our error message"))));
        }
        [Test]
        public void should_have_red_color_for_errors()
        {
            consolewrapper.VerifySet(console=>console.ForegroundColor = ConsoleColor.Red);
        }

        [Test]
        public void should_reset_color_for_other_specs()
        {
             consolewrapper.VerifySet(console=>console.ForegroundColor = ConsoleColor.DarkMagenta);
        }
    }
        public class BaseXmlReportContext: BaseReportFactoryContext
    {
        protected XElement _result;
        [SetUp]
        public override void SetUp()
        {
            base.SetUp();
            _result = RunXmlReport();
        }

        protected XAttribute[] GetAttributesFromElement(XElement element)
        {
            return element.Attributes().ToArray();
        }

        protected XElement[] GetTestCases()
        {
            return _result.Descendants().Where(x => x.Name.LocalName == "test-case").ToArray();
        }

        protected XElement RunXmlReport()
        {
            var consolewrapper = new StubFileWrapper();
            var reportfactory = new NUnitXmlReporter(consolewrapper);
            reportfactory.FromSpecContainers(spec);
            return XElement.Parse(consolewrapper.StringCalled);

        }
    }
    [TestFixture]
    public class SpecXmlReportFactoryWhenGeneratingReport : BaseXmlReportContext
    {


        [SetUp]
        public override void SetUp()
        {
            spec = IntializeSpec(RuleStatus.Passed);
            base.SetUp();

        }

        [Test]
        public void should_have_parent_test_results_element()
        {

            Assert.IsTrue(_result.Name.LocalName == "test-results");
        }

        [Test]
        public void should_have_number_of_total_specs_in_parent_attribute()
        {
            Assert.IsTrue(_result.Attribute("total").Value == "1");

        }
        [Test]
        public void should_have_test_case_element_for_each_rule()
        {

            Assert.AreEqual(1,GetTestCases().Count());
        }

        [Test]
        public void should_have_name_attribute_match_up_to_rule_name()
        {

            var testcaseattributes =  GetAttributesFromElement(GetTestCases().ToArray()[0]);
            Assert.AreEqual("Run Ofac Check",testcaseattributes.First(x=>x.Name.LocalName=="name").Value);
        }

        [Test]
        public void should_have_test_suite_element_wrap_rule_results()
        {
            var ruleresults = _result.XPathSelectElement("//test-suite/results/test-case");
            Assert.IsNotNull(ruleresults);


        }

        [Test]
        public void should_store_specobject_dot_context_under_name_attribute_of_testsuite_element()
        {

            var testsuite = _result.XPathSelectElement("//test-suite");
            var testsuiteattribs = GetAttributesFromElement(testsuite);
            var nameattrib = testsuiteattribs.Single(x => x.Name.LocalName == "name");
            Assert.AreEqual(nameattrib.Value, "BankAccount.When Totaling Transactions");
            
        }



      
    }
    [TestFixture]
    public class SpecXmlReportFactoryWhenGeneratingReportAndThereAreFailures : BaseXmlReportContext
    {
        

        [SetUp]
        public override void SetUp()
        {
            spec = IntializeSpec(RuleStatus.Failed);
            base.SetUp();
            
        }

        [Test]
        public void should_have_number_of_failed_specs_in_parent_attribute()
        {
            Assert.IsTrue(_result.Attribute("failures").Value == "1");

        }
    }
    [TestFixture]
    public class SpecXmlReportFactoryWhenGeneratingReportAndThereAreErrors : BaseXmlReportContext
    {


        [SetUp]
        public override void SetUp()
        {
            spec = IntializeSpec(RuleStatus.Error);
            base.SetUp();

        }

        [Test]
        public void should_have_number_of_failed_specs_in_parent_attribute()
        {
            Assert.IsTrue(_result.Attribute("failures").Value == "1");

        }
    }
    [TestFixture]
    public class SpecXmlReportFactoryWhenGeneratingReportAndThereArePending : BaseXmlReportContext
    {


        [SetUp]
        public override void SetUp()
        {
            spec = IntializeSpec(RuleStatus.Pending);
            base.SetUp();

        }
        [Test]
        public void should_have_number_of_non_specs_in_parent_attribute()
        {
            Assert.IsTrue(_result.Attribute("not-run").Value == "1");

        }
    }
    
}